#pragma once

#include "log.hpp"
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <functional>
#include <unordered_map>

using namespace std;
typedef function<string(string &)> fun_t;

Log lg;

const string defaultip = "0.0.0.0";
uint16_t defaultport = 8080;

class udpserver
{
public:
    udpserver(uint16_t port = defaultport, string ip = defaultip)
        : _sockfd(0),
          _ip(ip),
          _port(port)
    {
    }
    void Init()
    {
        // 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            lg(Fatal, "socket create failed, sockfd %d", _sockfd);
            exit(1);
        }
        lg(Info, "socket create success, sockfd %d", _sockfd);

        // bind
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);                  // host -> 网络字节序转换
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 将字符串转成uint32_t , 再转成网络字节序
        socklen_t len = sizeof(local);
        int b = bind(_sockfd, (const sockaddr *)&local, len);
        if (b < 0)
        {
            lg(Fatal, "bind failed, errno: %d, info: %s", errno, strerror(errno));
            exit(2);
        }
        lg(Info, "bind success, errno: %d, info: %s", errno, strerror(errno));
    }
    void check_user(const struct sockaddr_in &client, const string ip, uint16_t port)
    {
        // 如果这个用户不在就加进来，如果用户在就不做任何操作
        auto iter = _user_list.find(ip);
        if(iter == _user_list.end())
        {
            _user_list.insert({ip, client});
            std::cout << "[" << ip << ":" << port << "] add to online user." << std::endl;
        }
        
        // int f = 0;
        // for (auto &user : _user_list)
        // {
        //     if (ip == user.first)
        //     {
        //         f = 1;
        //         break;
        //     }
        // }
        // if (!f)
        // {
        //     cout << "[" << ip << ":" << to_string(port) << "] add to online user." << endl;
        //     _user_list[ip] = client;
        // }
    }

    void broadcast(const string &mess, const string &ip, const uint16_t port)
    {
        string recv_mess;
        recv_mess += "[";
        recv_mess += ip;
        recv_mess += ":";
        recv_mess += to_string(port);
        recv_mess += "]#";
        recv_mess += mess;
        for (auto &user : _user_list)
        {
            ssize_t s = sendto(_sockfd, recv_mess.c_str(), recv_mess.size(), 0, (const struct sockaddr *)&(user.second), sizeof(user.second));
        }
    }
    void Run()
    {
        char inbuffer[1024] = {0};
        while (true)
        {
            // 接收
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t v = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&client, &len);
            if (v < 0)
            {
                lg(Warning, "receive failed, errno: %d, info: %s", errno, strerror(errno));
                continue;
            }
            inbuffer[v] = 0; // 这里面存放着消息

            string c_ip = inet_ntoa(client.sin_addr); // 客户端的IP地址
            uint16_t c_port = ntohs(client.sin_port); // 客户端的端口
            check_user(client, c_ip, c_port);         // 判断这个客户端是否在用户列表里

            // 发送给其他用户
            string message = inbuffer;        // 要发送的消息
            broadcast(message, c_ip, c_port); // 将这条消息广播给用户列表中的所有用户

            // cout << "server get " << in << endl;
            // string cmd = func(in);
        }
    }
    ~udpserver()
    {
        if(_sockfd > 0) close(_sockfd);
    }

private:
    int _sockfd;                                          // 网络描述符,socket的返回值，类似于打开一个文件
    uint16_t _port;                                       // 端口
    string _ip;                                           // IP地址
    unordered_map<string, struct sockaddr_in> _user_list; // 用户列表
};